/**
 * 图片裁剪页面
 * 提供批量图片裁剪功能
 */
import React, { useState, useCallback, useRef, useEffect } from 'react';
import { Card, Button, message, InputNumber, Modal, Input, Slider, Row, Col, Space, Select, Progress, Switch, Typography } from 'antd';
import { PictureOutlined, DownloadOutlined, EyeOutlined, FolderOutlined, RedoOutlined, ZoomOutOutlined, ZoomInOutlined, FullscreenOutlined } from '@ant-design/icons';
import toolsService from '../../services/toolsService';
import { FileDropZone } from '../../components/common/FileDropZone';

interface SelectedFile {
  id: string;
  name: string;
  file: File;
  url: string; // 用于预览的URL
}

/**
 * 图片裁剪页面组件
 */
const ImageCropPage: React.FC = () => {
  // 状态管理
  const [selectedFiles, setSelectedFiles] = useState<SelectedFile[]>([]);
  const [processing, setProcessing] = useState(false);
  const [processingProgress, setProcessingProgress] = useState(0);
  const [currentProcessingFile, setCurrentProcessingFile] = useState<string>('');
  
  // 裁剪参数
  const [width, setWidth] = useState<number>(930);
  const [height, setHeight] = useState<number>(610);
  const [x, setX] = useState<number>(462);
  const [y, setY] = useState<number>(70);
  const [scale, setScale] = useState<number>(0.5);
  // whether a successful download/export has occurred (used to disable re-download until change)
  const [downloaded, setDownloaded] = useState<boolean>(false);
  const initialCropAppliedRef = useRef<boolean>(false);
  
  // 预览相关
  const [previewImage, setPreviewImage] = useState<string | null>(null);
  const [currentPreviewFile, setCurrentPreviewFile] = useState<SelectedFile | null>(null);
  const [cropPreviewUrl, setCropPreviewUrl] = useState<string | null>(null);
  // draggable/resizable crop states (image pixel coords)
  const containerRef = useRef<HTMLDivElement | null>(null);
  const imgRef = useRef<HTMLImageElement | null>(null);
  const [imgNaturalSize, setImgNaturalSize] = useState<{ w: number; h: number } | null>(null);
  const [containerSize, setContainerSize] = useState<{ w: number; h: number } | null>(null);
  const [isDragging, setIsDragging] = useState(false);
  const [isResizing, setIsResizing] = useState<null | string>(null);
  const dragStartRef = useRef<{ mx: number; my: number; x: number; y: number } | null>(null);
  const resizeStartRef = useRef<{ mx: number; my: number; x: number; y: number; w: number; h: number } | null>(null);
  
  // 配置相关
  const [savePath, setSavePath] = useState<string>('');
  
  // 宽高比控制
  const [aspectRatio, setAspectRatio] = useState<string>('free');
  const [isAspectLocked, setIsAspectLocked] = useState(false);
  // 解析数值宽高比（用于显示约束）
  const aspectNumber: number | undefined = aspectRatio !== 'free'
    ? (() => { const [w, h] = aspectRatio.split(':').map(Number); return w && h ? w / h : undefined; })()
    : undefined;
  
  // 引用
  const fileInputRef = useRef<HTMLInputElement>(null);
  const canvasRef = useRef<HTMLCanvasElement>(null);
  
  // 常用宽高比选项
  const aspectRatioOptions = [
    { value: 'free', label: '自由' },
    { value: '1:1', label: '1:1 (正方形)' },
    { value: '4:3', label: '4:3' },
    { value: '16:9', label: '16:9' },
    { value: '3:2', label: '3:2' },
    { value: '2:3', label: '2:3 (垂直)' },
    { value: '9:16', label: '9:16 (手机视频)' }
  ];
  
  // compute displayed image info
  const computeDisplay = useCallback(() => {
    if (!imgNaturalSize || !containerSize) return null;
    const s = Math.min(containerSize.w / imgNaturalSize.w, containerSize.h / imgNaturalSize.h);
    const dispW = imgNaturalSize.w * s;
    const dispH = imgNaturalSize.h * s;
    const offsetX = (containerSize.w - dispW) / 2;
    const offsetY = (containerSize.h - dispH) / 2;
    return { s, dispW, dispH, offsetX, offsetY };
  }, [imgNaturalSize, containerSize]);

  const startDrag = useCallback((e: React.MouseEvent) => {
    e.preventDefault();
    e.stopPropagation();
    setIsDragging(true);
    dragStartRef.current = { mx: e.clientX, my: e.clientY, x, y };
  }, [x, y]);

  const startResize = useCallback((handle: string) => (e: React.MouseEvent) => {
    e.preventDefault();
    e.stopPropagation();
    setIsResizing(handle);
    resizeStartRef.current = { mx: e.clientX, my: e.clientY, x, y, w: width, h: height };
  }, [x, y, width, height]);

  // global mouse move/up handlers for dragging/resizing
  useEffect(() => {
    if (!imgNaturalSize) return;
    const disp = computeDisplay();
    if (!disp) return;
    const s = disp.s;

    const onMove = (ev: MouseEvent) => {
      if (isDragging && dragStartRef.current) {
        const dx = (ev.clientX - dragStartRef.current.mx) / s;
        const dy = (ev.clientY - dragStartRef.current.my) / s;
        let nx = Math.round(dragStartRef.current.x + dx);
        let ny = Math.round(dragStartRef.current.y + dy);
        nx = Math.max(0, Math.min(nx, imgNaturalSize.w - width));
        ny = Math.max(0, Math.min(ny, imgNaturalSize.h - height));
        setX(nx);
        setY(ny);
      } else if (isResizing && resizeStartRef.current) {
        const dx = (ev.clientX - resizeStartRef.current.mx) / s;
        const dy = (ev.clientY - resizeStartRef.current.my) / s;
        let nx = resizeStartRef.current.x;
        let ny = resizeStartRef.current.y;
        let nw = resizeStartRef.current.w;
        let nh = resizeStartRef.current.h;

        switch (isResizing) {
          case 'e':
            nw = Math.round(Math.max(1, resizeStartRef.current.w + dx));
            break;
          case 's':
            nh = Math.round(Math.max(1, resizeStartRef.current.h + dy));
            break;
          case 'se':
            nw = Math.round(Math.max(1, resizeStartRef.current.w + dx));
            nh = Math.round(Math.max(1, resizeStartRef.current.h + dy));
            break;
          case 'w':
            nx = Math.round(resizeStartRef.current.x + dx);
            nw = Math.round(Math.max(1, resizeStartRef.current.w - dx));
            break;
          case 'n':
            ny = Math.round(resizeStartRef.current.y + dy);
            nh = Math.round(Math.max(1, resizeStartRef.current.h - dy));
            break;
          case 'nw':
            nx = Math.round(resizeStartRef.current.x + dx);
            ny = Math.round(resizeStartRef.current.y + dy);
            nw = Math.round(Math.max(1, resizeStartRef.current.w - dx));
            nh = Math.round(Math.max(1, resizeStartRef.current.h - dy));
            break;
          case 'ne':
            ny = Math.round(resizeStartRef.current.y + dy);
            nw = Math.round(Math.max(1, resizeStartRef.current.w + dx));
            nh = Math.round(Math.max(1, resizeStartRef.current.h - dy));
            break;
          case 'sw':
            nx = Math.round(resizeStartRef.current.x + dx);
            nw = Math.round(Math.max(1, resizeStartRef.current.w - dx));
            nh = Math.round(Math.max(1, resizeStartRef.current.h + dy));
            break;
        }

        // clamp
        if (nx < 0) { nw += nx; nx = 0; }
        if (ny < 0) { nh += ny; ny = 0; }
        if (nx + nw > imgNaturalSize.w) nw = imgNaturalSize.w - nx;
        if (ny + nh > imgNaturalSize.h) nh = imgNaturalSize.h - ny;

        setX(Math.max(0, nx));
        setY(Math.max(0, ny));
        setWidth(Math.max(1, nw));
        setHeight(Math.max(1, nh));
      }
    };

    const onUp = () => {
      setIsDragging(false);
      setIsResizing(null);
      dragStartRef.current = null;
      resizeStartRef.current = null;
    };

    if (isDragging || isResizing) {
      window.addEventListener('mousemove', onMove);
      window.addEventListener('mouseup', onUp);
    }
    return () => {
      window.removeEventListener('mousemove', onMove);
      window.removeEventListener('mouseup', onUp);
    };
  }, [isDragging, isResizing, computeDisplay, imgNaturalSize, containerSize, width, height, x, y]);

  // update container size on resize
  useEffect(() => {
    const update = () => {
      const rect = containerRef.current?.getBoundingClientRect();
      if (rect) setContainerSize({ w: rect.width, h: rect.height });
    };
    update();
    window.addEventListener('resize', update);
    return () => window.removeEventListener('resize', update);
  }, []);

  /**
   * 处理文件拖放或选择
   */
  const handleFilesDropped = useCallback((files: File[]) => {
    const imageFiles = files.filter(file => file.type.startsWith('image/'));
    
    if (imageFiles.length === 0) {
      message.warning('未检测到有效的图片文件！');
      return;
    }

    // 创建新的选中文件列表，包含预览URL
    const newSelectedFiles = imageFiles.map(file => ({
      id: `${file.name}-${Date.now()}-${Math.random()}`,
      name: file.name,
      file: file,
      url: URL.createObjectURL(file)
    }));

    setSelectedFiles(prev => {
      // 清理旧文件的URL对象
      prev.forEach(file => URL.revokeObjectURL(file.url));
      // new files -> clear downloaded state and reset initial crop marker
      initialCropAppliedRef.current = false;
      setDownloaded(false);
      return newSelectedFiles; // 替换而不是追加文件列表
    });
  }, []);

  /**
   * 移除文件
   */
  const handleRemoveFile = useCallback((id: string) => {
    setSelectedFiles(prev => {
      const fileToRemove = prev.find(file => file.id === id);
      if (fileToRemove) {
        URL.revokeObjectURL(fileToRemove.url);
      }
      return prev.filter(file => file.id !== id);
    });
  }, []);

  /**
   * 清空所有文件
   */
  const handleClearFiles = useCallback(() => {
    setSelectedFiles(prev => {
      // 清理所有文件的URL对象
      prev.forEach(file => URL.revokeObjectURL(file.url));
      return [];
    });
    setCurrentPreviewFile(null);
    if (previewImage) { URL.revokeObjectURL(previewImage); setPreviewImage(null); }
    if (cropPreviewUrl) { URL.revokeObjectURL(cropPreviewUrl); setCropPreviewUrl(null); }
    setDownloaded(false);
    initialCropAppliedRef.current = false;
  }, []);

  /**
   * 显示预览图片
   */
  const handlePreview = useCallback((url: string, file?: SelectedFile) => {
    setPreviewImage(url);
    if (file) {
      setCurrentPreviewFile(file);
    }
  }, []);

  /**
   * 生成裁剪预览图像
   */
  const generateCropPreview = useCallback(() => {
    if (!currentPreviewFile) return;

    const img = new Image();
    const canvas = document.createElement('canvas');
    const ctx = canvas.getContext('2d');
    
    if (!ctx) return;

    // 清理之前的预览URL
    if (cropPreviewUrl) {
      URL.revokeObjectURL(cropPreviewUrl);
    }

    img.onload = () => {
      // 设置canvas大小为预览尺寸
      const previewWidth = 400;
      const previewHeight = 300;
      canvas.width = previewWidth;
      canvas.height = previewHeight;

      // 计算缩放比例以适应预览窗口
      const imgRatio = img.width / img.height;
      const previewRatio = previewWidth / previewHeight;
      let drawWidth, drawHeight, offsetX = 0, offsetY = 0;

      if (imgRatio > previewRatio) {
        drawWidth = previewWidth;
        drawHeight = previewWidth / imgRatio;
        offsetY = (previewHeight - drawHeight) / 2;
      } else {
        drawHeight = previewHeight;
        drawWidth = previewHeight * imgRatio;
        offsetX = (previewWidth - drawWidth) / 2;
      }

      // 绘制原图
      ctx.drawImage(img, offsetX, offsetY, drawWidth, drawHeight);

      // 计算裁剪框在预览中的位置和大小
      const cropX = (x / img.width) * drawWidth + offsetX;
      const cropY = (y / img.height) * drawHeight + offsetY;
      const cropW = (width / img.width) * drawWidth;
      const cropH = (height / img.height) * drawHeight;

      // 如果宽高比被锁定且选择了特定比例，自动调整裁剪框以保持比例
      let displayCropW = cropW;
      let displayCropH = cropH;
      
      if (isAspectLocked && aspectRatio !== 'free') {
        const [w, h] = aspectRatio.split(':').map(Number);
        if (w && h) {
          // 保持宽度不变，调整高度
          displayCropH = (displayCropW * h) / w;
        }
      }
      
      // 绘制裁剪框（用虚线）
      ctx.strokeStyle = 'rgba(255, 0, 0, 0.8)';
      ctx.lineWidth = 2;
      ctx.setLineDash([5, 5]);
      ctx.strokeRect(cropX, cropY, displayCropW, displayCropH);
      ctx.setLineDash([]);

      // 绘制裁剪框四角标记
      const cornerSize = 8;
      ctx.fillStyle = 'rgba(255, 0, 0, 0.8)';
      // 左上角
      ctx.fillRect(cropX - cornerSize / 2, cropY - cornerSize / 2, cornerSize, cornerSize);
      // 右上角
      ctx.fillRect(cropX + displayCropW - cornerSize / 2, cropY - cornerSize / 2, cornerSize, cornerSize);
      // 左下角
      ctx.fillRect(cropX - cornerSize / 2, cropY + displayCropH - cornerSize / 2, cornerSize, cornerSize);
      // 右下角
      ctx.fillRect(cropX + displayCropW - cornerSize / 2, cropY + displayCropH - cornerSize / 2, cornerSize, cornerSize);

      // 创建预览URL
      canvas.toBlob((blob) => {
        if (blob) {
          const url = URL.createObjectURL(blob);
          setCropPreviewUrl(url);
        }
      });
    };

    img.src = currentPreviewFile.url;
  }, [currentPreviewFile, width, height, x, y, cropPreviewUrl]);

  // 从图片和裁剪像素区域生成裁剪后的 Blob
  const getCroppedImg = useCallback(async (imageSrc: string, cropX: number, cropY: number, cropW: number, cropH: number) => {
    const createImage = (url: string) => new Promise<HTMLImageElement>((resolve, reject) => {
      const img = new Image();
      img.crossOrigin = 'anonymous';
      img.onload = () => resolve(img);
      img.onerror = (e) => reject(e);
      img.src = url;
    });

    const image = await createImage(imageSrc);
    const canvas = document.createElement('canvas');
    canvas.width = Math.max(1, Math.round(cropW));
    canvas.height = Math.max(1, Math.round(cropH));
    const ctx = canvas.getContext('2d');
    if (!ctx) return null;

    ctx.drawImage(
      image,
      cropX,
      cropY,
      cropW,
      cropH,
      0,
      0,
      cropW,
      cropH
    );

    return new Promise<Blob | null>((resolve) => {
      canvas.toBlob((blob) => {
        resolve(blob);
      }, 'image/png');
    });
  }, []);

  // 处理宽高比选择变化
  const handleAspectRatioChange = useCallback((newRatio: string) => {
    setAspectRatio(newRatio);

    // 如果选择了特定比例，应用到当前宽高
    if (newRatio !== 'free' && width > 0) {
      const [w, h] = newRatio.split(':').map(Number);
      if (w && h) {
        setHeight(Math.round((width * h) / w));
      }
    }
  }, [width]);
  
  // 切换宽高比锁定状态
  const toggleAspectLock = useCallback((checked?: boolean) => {
    const newLockState = typeof checked === 'boolean' ? checked : !isAspectLocked;
    setIsAspectLocked(newLockState);

    // 锁定时，如果选择了特定比例，立即应用
    if (newLockState && aspectRatio !== 'free' && width > 0) {
      const [w, h] = aspectRatio.split(':').map(Number);
      if (w && h) {
        setHeight(Math.round((width * h) / w));
      }
    }
  }, [isAspectLocked, aspectRatio, width]);
  
  // 当宽高变化且宽高比锁定时，自动调整另一值
  // 宽高比联动调整
  useEffect(() => {
    if (isAspectLocked && aspectRatio !== 'free') {
      const [w, h] = aspectRatio.split(':').map(Number);
      if (w && h && width > 0) {
        // 确保高度符合宽高比
        setHeight(Math.round((width * h) / w));
      }
    }
  }, [isAspectLocked, aspectRatio, width]);
  
  useEffect(() => {
    if (isAspectLocked && aspectRatio !== 'free') {
      const [w, h] = aspectRatio.split(':').map(Number);
      if (w && h && height > 0) {
        // 确保宽度符合宽高比
        setWidth(Math.round((height * w) / h));
      }
    }
  }, [isAspectLocked, aspectRatio, height]);
  
  // 当相关参数变化时更新预览
  // 预览生成和更新
  useEffect(() => {
    generateCropPreview();
    
    // 清理函数
    return () => {
      if (cropPreviewUrl) {
        URL.revokeObjectURL(cropPreviewUrl);
      }
    };
  }, [generateCropPreview, cropPreviewUrl]);

  // 自动选择第一个文件作为预览
  useEffect(() => {
    if (selectedFiles.length > 0 && !currentPreviewFile) {
      setCurrentPreviewFile(selectedFiles[0]);
    }
  }, [selectedFiles, currentPreviewFile]);

  // 当图片加载并且尚未应用初始裁剪时，居中并设置一个合理的初始裁剪框
  useEffect(() => {
    if (!currentPreviewFile || !imgNaturalSize || !containerSize) return;
    if (initialCropAppliedRef.current) return;

    const iw = imgNaturalSize.w;
    const ih = imgNaturalSize.h;

    // 以图片中心为裁剪中心，使用当前宽高或缩放至适配
    const cw = Math.min(width, iw);
    const ch = Math.min(height, ih);
    const cx = Math.max(0, Math.round((iw - cw) / 2));
    const cy = Math.max(0, Math.round((ih - ch) / 2));

    setX(cx);
    setY(cy);
    setWidth(cw);
    setHeight(ch);
    initialCropAppliedRef.current = true;
  }, [currentPreviewFile, imgNaturalSize, containerSize]);

  /**
   * 选择保存路径
   */
  const handleSelectSavePath = useCallback(async () => {
    try {
      if ((window as any).electron?.ipc) {
        const result = await (window as any).electron.ipc.invoke('select-folder');
        if (result && result.success) {
          setSavePath(result.path);
        } else if (result && result.canceled) {
          // 用户取消选择，不执行任何操作
        } else {
          message.error('选择文件夹失败');
        }
      } else {
        message.warning('当前环境不支持选择目录');
      }
    } catch (error) {
      console.error('选择保存路径失败:', error);
      message.error('选择保存路径失败');
    }
  }, []);

  /**
   * 批量裁剪图片
   */
  const handleBatchCrop = useCallback(async () => {
    if (selectedFiles.length === 0) {
      message.warning('请先选择图片文件！');
      return;
    }

    // 如果指定了保存路径但路径不存在，提示用户
    if (savePath && !(window as any).electron?.ipc) {
      message.warning('当前环境不支持保存到指定路径，将使用浏览器下载');
    }

    setProcessing(true);
    setProcessingProgress(0);
    setCurrentProcessingFile('');

    try {
      // 提取File对象
      const fileObjects: File[] = selectedFiles.map(file => file.file);
      
      // 进度更新函数
      const updateProgress = (currentIndex: number, fileName: string) => {
        const progress = Math.round(((currentIndex + 1) / fileObjects.length) * 100);
        setProcessingProgress(progress);
        setCurrentProcessingFile(fileName);
      };
      
      // 使用工具服务批量裁剪图片
      const croppedImages = await toolsService.batchCropImages(fileObjects, {
        width,
        height,
        x,
        y,
        onProgress: (progress: number) => {
            // 由于API不提供index信息，我们无法更新具体文件的进度
            // 简单地更新整体进度
        }
      });

      // 如果设置了保存路径且支持Electron API，则保存到指定路径
      if (savePath && (window as any).electron?.ipc) {
        // 使用Electron API保存到指定路径 - 使用顺序处理以显示更准确的进度
        const saveResults = [];
        for (let i = 0; i < croppedImages.length; i++) {
          try {
            const croppedImage = croppedImages[i];
            updateProgress(i, selectedFiles[i].name);
            
            const fileName = croppedImage.file.name.replace(/\.[^/.]+$/, "") + "_cropped." + croppedImage.file.name.split('.').pop();
            const fullPath = `${savePath}/${fileName}`;
            const arrayBuffer = await croppedImage.blob.arrayBuffer();
            const buffer = Buffer.from(arrayBuffer);
            await (window as any).electron.ipc.invoke('save-file-buffer', { path: fullPath, buffer });
            saveResults.push({ success: true, fileName });
          } catch (error) {
            console.error(`保存文件失败:`, error);
            saveResults.push({ success: false, fileName: selectedFiles[i].name, error });
          }
        }

        const successCount = saveResults.filter(r => r.success).length;
        const failCount = saveResults.length - successCount;

        if (failCount > 0) {
          message.warning(`成功保存 ${successCount} 个文件，${failCount} 个文件保存失败`);
        } else {
          message.success(`成功保存 ${successCount} 个文件到: ${savePath}`);
        }
      } else {
        // 回退到浏览器下载方式 - 顺序处理以显示更准确的进度
        for (let i = 0; i < croppedImages.length; i++) {
          const croppedImage = croppedImages[i];
          updateProgress(i, selectedFiles[i].name);
          
          const url = URL.createObjectURL(croppedImage.blob);
          const a = document.createElement('a');
          a.href = url;
          a.download = croppedImage.file.name.replace(/\.[^/.]+$/, "") + "_cropped." + croppedImage.file.name.split('.').pop();
          document.body.appendChild(a);
          a.click();
          document.body.removeChild(a);
          URL.revokeObjectURL(url);
          
          // 为了让用户看到进度变化，添加短暂延迟
          if (i < croppedImages.length - 1) {
            await new Promise(resolve => setTimeout(resolve, 100));
          }
        }
        message.success(`成功处理 ${fileObjects.length} 张图片！已触发浏览器下载。`);
      }

  // 标记已下载，保留文件列表以便用户可继续编辑或清除
  setDownloaded(true);
    } catch (error) {
      console.error('图片处理失败:', error);
      message.error('图片处理失败，请重试！');
    } finally {
      setProcessing(false);
      setProcessingProgress(0);
      setCurrentProcessingFile('');
    }
  }, [selectedFiles, width, height, x, y, handleClearFiles, savePath]);

  /**
   * 组件卸载时清理URL对象
   */
  // 组件卸载时清理资源
  useEffect(() => {
    return () => {
      selectedFiles.forEach(file => URL.revokeObjectURL(file.url));
      if (previewImage) {
        URL.revokeObjectURL(previewImage);
      }
    };
  }, [selectedFiles, previewImage]);

  return (
    <div className="app-page" style={{ padding: 0 }}>
      <Row>
        <Col span={24}>
          <Card 
            className="image-crop-card flex flex-col gap-4 py-4 px-4 shadow-sm h-full"
            style={{ minWidth: 0 }}
            title={
              <Space>
                <PictureOutlined style={{ color: 'var(--app-primary-color)' }} />
                批量图片裁剪
              </Space>
            }
          >

            <div className="card-content space-y-4 transition-all duration-300 flex-1 flex flex-col">
              <div>
                <FileDropZone
                  onFilesDropped={handleFilesDropped}
                  title="点击或拖拽图片文件到此处"
                  description="支持JPG、PNG、GIF等常见图片格式"
                  disabled={processing}
                />
              </div>
              
              {/* 保存路径选择 */}
              <div>
                <div style={{ marginBottom: '8px' }}>
                  <strong>保存路径：</strong>
                </div>
                <div style={{ display: 'flex', gap: '8px' }}>
                  <Input 
                    placeholder="选择保存路径" 
                    value={savePath} 
                    onChange={e => setSavePath(e.target.value)}
                    disabled={processing}
                    size="large"
                    style={{ flex: 1 }}
                  />
                  <Button 
                    icon={<FolderOutlined />} 
                    onClick={handleSelectSavePath}
                    disabled={processing}
                    size="large"
                  >
                    选择
                  </Button>
                </div>
              </div>
              
              {/* 实时预览区域 */}
              {currentPreviewFile && (
                <div className="preview-section border rounded-md p-4">
                  <div className="text-sm font-medium mb-2">实时预览</div>
                  <div ref={containerRef} className="relative bg-gray-100 rounded-md overflow-hidden flex items-center justify-center" style={{ height: '360px', minHeight: '200px' }}>
                    <div style={{ position: 'relative', width: '100%', height: '100%' }}>
                      <img
                        ref={imgRef}
                        src={currentPreviewFile.url}
                        alt={currentPreviewFile.name}
                        style={{ maxWidth: '100%', maxHeight: '100%', objectFit: 'contain', display: 'block', width: '100%', height: '100%' }}
                        onLoad={(e) => {
                          const imgEl = e.currentTarget as HTMLImageElement;
                          setImgNaturalSize({ w: imgEl.naturalWidth, h: imgEl.naturalHeight });
                          // compute container size
                          const rect = containerRef.current?.getBoundingClientRect();
                          if (rect) setContainerSize({ w: rect.width, h: rect.height });
                        }}
                      />

                      {/* overlay and crop rect */}
                      {imgNaturalSize && containerSize && (
                        (() => {
                          const scale = Math.min(containerSize.w / imgNaturalSize.w, containerSize.h / imgNaturalSize.h);
                          const dispW = imgNaturalSize.w * scale;
                          const dispH = imgNaturalSize.h * scale;
                          const offsetX = (containerSize.w - dispW) / 2;
                          const offsetY = (containerSize.h - dispH) / 2;

                          const dispCropX = offsetX + x * scale;
                          const dispCropY = offsetY + y * scale;
                          const dispCropW = Math.max(8, width * scale);
                          const dispCropH = Math.max(8, height * scale);

                          const startDrag = (e: React.MouseEvent) => {
                            e.stopPropagation();
                            setIsDragging(true);
                            dragStartRef.current = { mx: e.clientX, my: e.clientY, x, y };
                          };

                          const startResize = (handle: string) => (e: React.MouseEvent) => {
                            e.stopPropagation();
                            setIsResizing(handle);
                            resizeStartRef.current = { mx: e.clientX, my: e.clientY, x, y, w: width, h: height };
                          };

                          

                          return (
                            <>
                              {/* dimmed overlay around image */}
                              <div style={{ position: 'absolute', left: offsetX, top: offsetY, width: dispW, height: dispH, pointerEvents: 'none' }} />

                              {/* crop rectangle */}
                              <div
                                onMouseDown={startDrag}
                                style={{
                                  position: 'absolute',
                                  left: dispCropX,
                                  top: dispCropY,
                                  width: dispCropW,
                                  height: dispCropH,
                                  border: '2px dashed rgba(255,0,0,0.9)',
                                  boxSizing: 'border-box',
                                  cursor: 'move',
                                  background: 'transparent'
                                }}
                              >
                                {/* handles */}
                                {['nw','n','ne','e','se','s','sw','w'].map(h => (
                                  <div
                                    key={h}
                                    onMouseDown={startResize(h)}
                                    style={{
                                      position: 'absolute',
                                      width: 10,
                                      height: 10,
                                      background: 'rgba(255,255,255,0.9)',
                                      border: '1px solid rgba(0,0,0,0.3)',
                                      borderRadius: 2,
                                      transform: 'translate(-50%, -50%)',
                                      ...(h === 'nw' ? { left: 0, top: 0 } : {}),
                                      ...(h === 'n' ? { left: '50%', top: 0 } : {}),
                                      ...(h === 'ne' ? { left: '100%', top: 0 } : {}),
                                      ...(h === 'e' ? { left: '100%', top: '50%' } : {}),
                                      ...(h === 'se' ? { left: '100%', top: '100%' } : {}),
                                      ...(h === 's' ? { left: '50%', top: '100%' } : {}),
                                      ...(h === 'sw' ? { left: 0, top: '100%' } : {}),
                                      ...(h === 'w' ? { left: 0, top: '50%' } : {})
                                    }}
                                  />
                                ))}
                              </div>

                              {/* info + actions */}
                              <div style={{ position: 'absolute', bottom: 8, left: 8, right: 8, display: 'flex', justifyContent: 'space-between', gap: 8 }}>
                                <div style={{ background: 'rgba(255,255,255,0.85)', padding: '6px 8px', borderRadius: 6, fontSize: 12 }}>
                                  裁剪区域: {width} × {height}px | 位置: ({x}, {y}) {isAspectLocked && aspectRatio !== 'free' ? `🔒 ${aspectRatio}` : ''}
                                </div>
                                <div style={{ display: 'flex', gap: 8 }}>
                                  <Button size="small" onClick={async () => {
                                    if (!currentPreviewFile) return;
                                    const blob = await getCroppedImg(currentPreviewFile.url, x, y, width, height);
                                    if (blob) {
                                      if (cropPreviewUrl) URL.revokeObjectURL(cropPreviewUrl);
                                      const url = URL.createObjectURL(blob);
                                      setCropPreviewUrl(url);
                                      setPreviewImage(url);
                                    } else {
                                      message.error('生成裁剪预览失败');
                                    }
                                  }}>
                                    生成预览
                                  </Button>
                                  <Button size="small" onClick={() => {
                                    // reset to defaults
                                    setWidth(930); setHeight(610); setX(462); setY(70);
                                  }}>
                                    复位裁剪
                                  </Button>
                                </div>
                              </div>
                            </>
                          );
                        })()
                      )}
                    </div>
                  </div>
                </div>
              )}
              
              {/* 文件预览列表 */}
              {selectedFiles.length > 0 && (
                <div className="file-list border rounded-md p-4 max-h-60 overflow-y-auto">
                  <div className="flex justify-between items-center mb-2">
                    <span className="font-medium">已选择 {selectedFiles.length} 个文件:</span>
                    <Button type="link" onClick={handleClearFiles} disabled={processing}>
                      清空
                    </Button>
                  </div>
                  <div className="space-y-2">
                    {selectedFiles.map(file => (
                      <div key={file.id} className="flex items-center justify-between p-2 bg-gray-50 rounded">
                        <div className="flex items-center">
                          <img 
                        src={file.url} 
                        alt={file.name} 
                        className="file-thumbnail rounded mr-2"
                        style={{ width: 32, height: 32, objectFit: 'cover' }}
                      />
                          <span className="text-sm truncate max-w-xs">{file.name}</span>
                        </div>
                        <div>
                          <Button 
                            type="text" 
                            size="small" 
                            icon={<EyeOutlined />}
                            onClick={() => handlePreview(file.url, file)}
                          />
                          <Button 
                            type="text" 
                            size="small" 
                            onClick={() => handleRemoveFile(file.id)}
                            disabled={processing}
                          >
                            移除
                          </Button>
                        </div>
                      </div>
                    ))}
                  </div>
                </div>
              )}
              
              {/* 裁剪参数设置区域 */}
              <div className="crop-parameters">
                <div style={{ marginBottom: '16px' }}>
                  <strong>裁剪参数：</strong>
                </div>
                
                <div className="grid grid-cols-1 sm:grid-cols-2 gap-4">
                  {/* 宽度和高度输入框 */}
                  <div className="space-y-2">
                    <label className="text-sm font-medium">宽度 (px)</label>
                    <InputNumber 
                      className="w-full"
                      min={1} 
                      value={width} 
                      onChange={(value) => setWidth(value === null ? 1 : value)} 
                      style={{ width: '100%' }}
                    />
                  </div>
                  
                  <div className="space-y-2">
                    <label className="text-sm font-medium">高度 (px)</label>
                    <InputNumber 
                      className="w-full"
                      min={1} 
                      value={height} 
                      onChange={(value) => setHeight(value === null ? 1 : value)} 
                      style={{ width: '100%' }}
                    />
                  </div>
                  
                  {/* X和Y坐标输入框 */}
                  <div className="space-y-2">
                    <label className="text-sm font-medium">X坐标 (px)</label>
                    <InputNumber 
                      className="w-full"
                      min={0} 
                      value={x} 
                      onChange={(value) => setX(value === null ? 0 : value)} 
                      style={{ width: '100%' }}
                    />
                  </div>
                  
                  <div className="space-y-2">
                    <label className="text-sm font-medium">Y坐标 (px)</label>
                    <InputNumber 
                      className="w-full"
                      min={0} 
                      value={y} 
                      onChange={(value) => setY(value === null ? 0 : value)} 
                      style={{ width: '100%' }}
                    />
                  </div>
                </div>
                
                {/* 宽高比选择和锁定 */}
                <div className="aspect-ratio-section space-y-2" style={{ marginTop: '16px' }}>
                  <label className="text-sm font-medium">宽高比</label>
                  <div className="flex flex-col sm:flex-row gap-2">
                    <Select
                      value={aspectRatio}
                      onChange={handleAspectRatioChange}
                      options={aspectRatioOptions.map(option => ({ label: option.label, value: option.value }))}
                      style={{ flex: 1 }}
                    />
                    <div style={{ display: 'flex', alignItems: 'center', gap: 8, minWidth: 120, justifyContent: 'flex-end' }}>
                      <Typography.Text type="secondary" style={{ whiteSpace: 'nowrap' }}>{isAspectLocked ? '已锁定' : '未锁定'}</Typography.Text>
                      <Switch checked={isAspectLocked} onChange={toggleAspectLock} />
                    </div>
                  </div>
                </div>
                
                {/* 缩放控制 */}
                <div className="scale-control-section space-y-2" style={{ marginTop: '16px' }}>
                  <label className="text-sm font-medium">缩放控制</label>
                  <div className="space-y-2">
                    <div className="flex items-center justify-between text-sm">
                      <span>当前缩放:</span>
                      <span className="font-medium">{Math.round(scale * 100)}%</span>
                    </div>
                    <div className="flex items-center gap-2">
                      <ZoomOutOutlined className="lucide lucide-zoom-out w-4 h-4 text-muted-foreground" />
                      <Slider 
                        min={0.1} 
                        max={5} 
                        step={0.1} 
                        value={scale} 
                        onChange={setScale} 
                        className="flex-1 h-2 bg-gray-200 rounded-lg appearance-none cursor-pointer disabled:cursor-not-allowed disabled:opacity-50"
                      />
                      <ZoomInOutlined className="lucide lucide-zoom-in w-4 h-4 text-muted-foreground" />
                    </div>
                    <div className="grid grid-cols-2 sm:grid-cols-4 gap-1">
                      <Button 
                        className="preset-button" 
                        title="缩小" 
                        icon={<ZoomOutOutlined />}
                        onClick={() => setScale(prev => Math.max(0.1, prev - 0.1))}
                      />
                      <Button 
                        className="preset-button" 
                        title="放大" 
                        icon={<ZoomInOutlined />}
                        onClick={() => setScale(prev => Math.min(5, prev + 0.1))}
                      />
                      <Button 
                        className="preset-button" 
                        title="100%"
                        onClick={() => setScale(1)}
                      >1:1</Button>
                      <Button 
                        className="preset-button" 
                        title="适应窗口" 
                        icon={<FullscreenOutlined />}
                        onClick={() => {
                          if (currentPreviewFile) {
                            // 创建临时图像对象以获取原始尺寸
                            const img = new Image();
                            img.onload = () => {
                              // 假设预览区域大小为400x300
                              const previewWidth = 400;
                              const previewHeight = 300;
                              
                              // 计算适应窗口的缩放比例
                              const scaleX = previewWidth / img.width;
                              const scaleY = previewHeight / img.height;
                              const fitScale = Math.min(scaleX, scaleY);
                              
                              // 设置缩放比例，确保在有效范围内
                              setScale(Math.min(5, Math.max(0.1, fitScale)));
                            };
                            img.src = currentPreviewFile.url;
                          }
                        }}
                      />
                    </div>
                  </div>
                </div>
                
                {/* 常用尺寸 */}
                <div className="preset-sizes-section space-y-2" style={{ marginTop: '16px' }}>
                  <label className="text-sm font-medium">常用尺寸</label>
                  <div className="grid grid-cols-2 gap-2 text-xs sm:text-sm">
                    <Button 
                      className="preset-button" 
                      onClick={() => {
                        setWidth(200);
                        setHeight(200);
                      }}
                    >
                      200×200
                    </Button>
                    <Button 
                      className="preset-button" 
                      onClick={() => {
                        setWidth(400);
                        setHeight(300);
                      }}
                    >
                      400×300
                    </Button>
                    <Button 
                      className="preset-button" 
                      onClick={() => {
                        setWidth(800);
                        setHeight(600);
                      }}
                    >
                      800×600
                    </Button>
                    <Button 
                      className="preset-button" 
                      onClick={() => {
                        setWidth(1920);
                        setHeight(1080);
                      }}
                    >
                      1920×1080
                    </Button>
                  </div>
                </div>
              </div>
              
              {/* 处理进度显示 */}
              {processing && (
                <div className="progress-section mt-4">
                  <div className="text-sm font-medium mb-2">处理进度</div>
                  <Progress percent={processingProgress} status={processingProgress < 100 ? 'active' : 'normal'} />
                  <div className="flex justify-between text-xs text-gray-600 mt-2">
                    <span>正在处理: {currentProcessingFile || '准备中...'}</span>
                    <span>{processingProgress}%</span>
                  </div>
                </div>
              )}
              
              <div className="flex-1"></div>
              
              <div className="space-y-2 mt-auto">
                <Button 
                  className="primary-button w-full"
                  icon={<DownloadOutlined />}
                  onClick={handleBatchCrop}
                  loading={processing}
                  disabled={selectedFiles.length === 0 || downloaded}
                >
                  下载图片
                </Button>
                <Button 
                  className="clear-button w-full"
                  danger
                  onClick={handleClearFiles}
                  disabled={selectedFiles.length === 0 && !previewImage}
                >
                  清除图片
                </Button>
                <Button 
                  className="reset-button w-full"
                  icon={<RedoOutlined />}
                  onClick={() => {
                    setWidth(930);
                    setHeight(610);
                    setX(462);
                    setY(70);
                    setScale(0.5);
                  }}
                >
                  重置
                </Button>
              </div>
            </div>
          </Card>
        </Col>
      </Row>

      {/* 使用说明 */}
      <Row style={{ marginTop: '24px' }}>
        <Col span={24}>
          <Card title="使用说明" type="inner">
            <Space style={{ width: '100%' }}>
              <div>
                <strong>批量图片裁剪：</strong>
                <ul>
                  <li>支持拖拽图片到上方区域进行批量处理</li>
                  <li>也可以输入保存路径指定裁剪后图片的保存位置</li>
                  <li>可调整裁剪区域的大小和位置</li>
                  <li>支持多种宽高比设置，可锁定比例进行裁剪</li>
                  <li>实时预览裁剪效果</li>
                  <li>支持常用尺寸快速设置</li>
                  <li>处理完成后会自动下载或保存到指定路径</li>
                </ul>
              </div>
            </Space>
          </Card>
        </Col>
      </Row>
    </div>
  );
}

export default ImageCropPage;